package great.easychat.help.config;

import android.os.Build;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.pince.http.DefaultHttpConfig;
import com.pince.http.GzipInterceptor;
import com.pince.renovace2.Renovace;
import com.pince.renovace2.Util.RenovaceLogUtil;
import com.pince.renovace2.interceptor.CacheInterceptor;
import com.pince.renovace2.interceptor.MainInterceptor;
import com.pince.ut.AppCache;
import com.pince.ut.AppUtil;
import com.pince.ut.OsInfoUtil;
import com.pince.ut.TimeUtil;
import com.pince.ut.constans.FileConstants;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import great.easychat.help.Constant;
import great.easychat.help.uinque.UUIDUtils;
import great.easychat.help.util.DebugLog;
import great.easychat.help.util.Util;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import retrofit2.Converter;
import retrofit2.Retrofit;

/**
 * @author athoucai
 * @date 2018/11/27
 */
public class AppHttpConfig extends DefaultHttpConfig {

    @Override
    protected String getBaseUrl() {
        return "https://i333.fanqiezhib.com";
    }

    @Override
    protected String cachePath() {
        return FileConstants.CACHE_NETWORK_DIR;
    }

    @Override
    public OkHttpClient client() {
        OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
        okHttpClientBuilder.addInterceptor(new MainInterceptor());
        okHttpClientBuilder.addInterceptor(new RequestInterceptor());
        client(okHttpClientBuilder);
        okHttpClientBuilder.addNetworkInterceptor(new CustomLogInterceptor());
        okHttpClientBuilder.addInterceptor(new CacheInterceptor());
//        okHttpClientBuilder.cache(HttpCache.getCache(cachePath()));
        okHttpClientBuilder.retryOnConnectionFailure(true);
        okHttpClientBuilder.connectTimeout(5000, TimeUnit.MILLISECONDS);
        return okHttpClientBuilder.build();
    }

    @Override
    public void client(OkHttpClient.Builder builder) {
        builder.addNetworkInterceptor(new GzipInterceptor());
    }

    @Override
    protected List<Converter.Factory> getConverterFactories() {
        List<Converter.Factory> factories = new ArrayList<>();
        factories.add(new AppConverFc(Renovace.getGson()));
        return factories;
    }


    private interface IRequestParam {
        Request getRequest(Request request, Map<String, String> params);
    }

    private static class ParamsContext {

        private IRequestParam iRequestParam;
        private Request request;
        private Map<String, String> parmas;

        public ParamsContext(Request request, Map<String, String> parmas) {
            this.request = request;
            this.parmas = parmas;
        }

        public Request getInRequest() {
            switch (request.method().toUpperCase()) {
                case "GET":
                    iRequestParam = new GetRequestParams();
                    break;
                case "POST":
                    iRequestParam = new PostRequestParams();
                    break;
                default:
                    throw new RuntimeException("not support request method");
            }
            return iRequestParam.getRequest(request, parmas);
        }
    }

    private static class AppConverFc extends Converter.Factory {

        private Gson mGson;

        public AppConverFc(Gson gson) {
            this.mGson = gson;
        }

        @Override
        public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                                Retrofit retrofit) {
            TypeAdapter<?> adapter = mGson.getAdapter(TypeToken.get(type));
            return new GsonResponseBodyConverter<>(mGson, type, adapter);
        }

        @Override
        public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                              Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
            TypeAdapter<?> adapter = mGson.getAdapter(TypeToken.get(type));
            return new GsonRequestBodyConverter<>(mGson, adapter);
        }
    }

    private static class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
        private final Gson gson;
        private final TypeAdapter<T> adapter;
        private final Type type;

        GsonResponseBodyConverter(Gson gson, Type type, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
            this.type = type;
        }

        @Override
        public T convert(ResponseBody value) throws IOException {
            String result = value.string();
            value = ResponseBody.create(value.contentType(), result);
            if (type.toString().equals("class java.lang.String")) {
                return (T) result;
            }
            JsonReader jsonReader = gson.newJsonReader(value.charStream());
            try {
                T result2 = adapter.read(jsonReader);
                if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
                    throw new JsonIOException("JSON document was not fully consumed.");
                }
                return result2;
            } finally {
                value.close();
            }
        }
    }

    private static final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
        private static final Charset UTF_8 = Charset.forName("UTF-8");

        private final Gson gson;
        private final TypeAdapter<T> adapter;

        GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
            this.gson = gson;
            this.adapter = adapter;
        }

        @Override
        public RequestBody convert(T value) throws IOException {
            Buffer buffer = new Buffer();
            Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
            JsonWriter jsonWriter = gson.newJsonWriter(writer);
            adapter.write(jsonWriter, value);
            jsonWriter.close();
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
        }
    }

    private static void printParams(String url, Map<String, String> params, String sign) {
        if (params != null && params.size() > 0) {
            StringBuilder paramSb = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                paramSb.append("\n");
                paramSb.append(entry.getKey() + "=" + entry.getValue());
            }
            paramSb.append("\n");
            paramSb.append("sign" + "=" + sign);
            RenovaceLogUtil.logD(String.format("Url=%s\nParams: {%s}", url, paramSb.toString()));
        }
    }

    private static class GetRequestParams implements IRequestParam {

        @Override
        public Request getRequest(Request request, Map<String, String> params) {
            HttpUrl oriHttpUrl = request.url();
            HttpUrl.Builder httpUrlBuilder = oriHttpUrl.newBuilder();
            StringBuilder stringBuilder = new StringBuilder();
            //添加公共参数
            for (String key : params.keySet()) {
                httpUrlBuilder.addEncodedQueryParameter(key, params.get(key));

            }
            //收集所有参数
            for (int i = 0; i < oriHttpUrl.querySize(); i++) {
                String key = oriHttpUrl.queryParameterName(i);
                String value = oriHttpUrl.queryParameterValue(i);
                params.put(key, value);
            }

            for(Map.Entry<String, String> entry : params.entrySet()){
                String key = entry.getKey();
                String value = entry.getValue();
                stringBuilder.append("&" + key + "=" + value);
            }
            String pa = stringBuilder.toString().substring(1);
            //将所有参数做加密生成一个sign字符串，并添加到参数列表中
            String sign="";
            if (oriHttpUrl.host().startsWith("hd215.api.yesapi.cn")) {
                sign = getSign(pa);
            } else {

            }
            httpUrlBuilder.addQueryParameter("sign", sign);

            //打印出所有参数
            printParams(oriHttpUrl.uri().toString(), params, sign);

            return request.newBuilder()
                    .url(httpUrlBuilder.build())
                    .build();
        }
    }

    private static class PostRequestParams implements IRequestParam {

        @Override
        public Request getRequest(Request request, Map<String, String> params) {
            RequestBody oriBody = request.body();
            if (oriBody instanceof FormBody) {
                FormBody.Builder bodyBuilder = new FormBody.Builder();
                StringBuilder stringBuilder = new StringBuilder();
                FormBody formBody = (FormBody) request.body();
                //收集所有参数
                for (int i = 0; i < formBody.size(); i++) {
                    String key = formBody.name(i);
                    String value = formBody.value(i);
                    params.put(key, value);
                }
                //添加公共参数到新的body
                for (String key : params.keySet()) {
                    bodyBuilder.add(key, params.get(key));
                }
                for(Map.Entry<String, String> entry : params.entrySet()){
                    String key = entry.getKey();
                    String value = entry.getValue();
                    stringBuilder.append("&" + key + "=" + value);
                }
                //将所有参数做加密生成一个sign字符串，并添加到参数列表中
                String pa = stringBuilder.toString().substring(1);
                String sign = getSign(pa);
                formBody = bodyBuilder.add("sign", sign)
                        .build();

                //打印出所有参数
                printParams(request.url().uri().toString(), params, sign);

                request = request.newBuilder()
                        .post(formBody)
                        .build();
            }
            return request;
        }
    }

    private static class RequestInterceptor implements Interceptor {

        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request().newBuilder()
                    .build();
            //添加公共参数
            TreeMap<String, String> params = new TreeMap<>(new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    return o1.compareTo(o2);
                }
            });
//            params.put("device", OsInfoUtil.getPhoneModel());

            //配置公共参数
            request = new ParamsContext(request, params).getInRequest();
            return chain.proceed(request);
        }
    }

    private static String getSign(String url) {
        try {
            DebugLog.d("getSign " + url);
            String u = url.substring(url.indexOf("?") + 1);
            String[] s = u.split("&");
            //treeMap默认是key升序排序 ,如果需要降序,可以使用Comparator中的compare方法
            TreeMap<String, String> map = new TreeMap<String, String>();
            for (String s1 : s) {
                if (TextUtils.isEmpty(s1) || !s1.contains("=")) {
                    continue;
                }
                String key = s1.substring(0, s1.indexOf("="));
                String value = s1.substring(s1.indexOf("=") + 1);
                map.put(key, value);
//                DebugLog.d("getSign1 " + key + " --- " + value);
            }
            StringBuffer sb = new StringBuffer();
            for (Map.Entry entry : map.entrySet()) {
//                DebugLog.d("getSign3 " + entry.getKey());
                sb = sb.append(entry.getValue());
//                DebugLog.d("getSign4 " + entry.getValue());
            }
            sb.append(Constant.XIAOBAI_APP_SECRECT);
            DebugLog.d("getSign2 " + sb.toString());
            String sign = Util.md5(sb.toString()).toUpperCase();
            return sign;
        } catch (Exception e) {
            return "";
        }
    }


}

